/*
 * Copyright 2025 arisgo@163.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.arisgo.cloud.core.id;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 雪花算法
 * 0-0000000 00000000 00000000 00000000 00000000 00-000000 0000-0000 00000000
 * 符号位 / 41bit时间戳 / 10bit机器位 / 12bit序列号
 * SnowFlake生成64bit大小整数，分为4个部分：
 * 一、1bit，不用，因为二进制中最高位是符号位，1表示负数，0表示正数。生成的id一般都是用整数，所以最高位固定为0。
 * 二、41bit-时间戳，用来记录时间戳，毫秒级。
 * - 41位可以表示个数字，
 * - 如果只用来表示正整数（计算机中正数包含0），可以表示的数值范围是：0 至 ，减1是因为可表示的数值范围是从0开始算的，而不是1。
 * - 也就是说41位可以表示个毫秒的值，转化成单位年则是年
 * 三、10bit-工作机器id，用来记录工作机器id。
 * - 可以部署在个节点，包括5位datacenterId和5位workerId
 * - 5位（bit）可以表示的最大正整数是，即可以用0、1、2、3、....31这32个数字，来表示不同的datecenterId或workerId
 * 四、12bit-序列号，序列号，用来记录同毫秒内产生的不同id。
 * - 12位（bit）可以表示的最大正整数是，即可以用0、1、2、3、....4094这4095个数字，来表示同一机器同一时间截（毫秒)内产生的4095个ID序号
 *
 * @author Arisgo
 * @code SnowFlakeIDKeyGenerator
 * @since 2024/6/11
 */
@Component
public class SnowFlakeGenerator {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 时间锚点，可有可无
     */
    private static final long TIME_ANCHOR = 171720000000L;
    private static final long MAX_SEQUENCE = ~(-1L << 12);

    /**
     * 序列号
     */
    private long sequence = 0;
    /**
     * 机房编号
     */
    @Value("${arisgo.cloud.snow.flake.worker-number:1}")
    private long workerNumber;
    /**
     * 服务器编号
     */
    @Value("${arisgo.cloud.snow.flake.server-number:1}")
    private long serverNumber;

    public SnowFlakeGenerator() {
        // sanity check for workerId
        long maxWorkNum = ~(-1L << 5);
        if (this.workerNumber > maxWorkNum || workerNumber < 0) {
            String msg = String.format("worker number can't be greater than %d or less than 0", maxWorkNum);
            logger.error(msg);
            throw new IllegalArgumentException(msg);
        }
        // 机房中服务器上限
        long maxServerNum = ~(-1L << 5);
        if (serverNumber > maxServerNum || serverNumber < 0) {
            String msg = String.format("server number can't be greater than %d or less than 0", maxServerNum);
            logger.error(msg);
            throw new IllegalArgumentException(msg);
        }

        this.lastTimestamp = System.currentTimeMillis();
        logger.debug("snow flake init info. init time {}, worker number {}, server number {}, sequence start {}", lastTimestamp, workerNumber, serverNumber, sequence);
    }

    /**
     * 保证同一台服务器生成的id不会回溯
     */
    private long lastTimestamp;

    public long getWorkerNumber() {
        return workerNumber;
    }

    public long getServerNumber() {
        return serverNumber;
    }

    public long getTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * id生成
     */
    public synchronized long generatorId() {
        long currentTimestamp = System.currentTimeMillis();

        //获取当前时间戳如果小于上次时间戳，则表示时间戳获取出现异常
        if (currentTimestamp < lastTimestamp) {
            logger.error("clock is moving backwards.  Rejecting requests until {}.", lastTimestamp);
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                    lastTimestamp - currentTimestamp));
        }

        //获取当前时间戳如果等于上次时间戳（同一毫秒内），则在序列号加一；否则序列号赋值为0，从0开始。
        if (lastTimestamp == currentTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                // 同一时间生成大量的id，导致sequence 溢出，需要等待下一毫秒
                currentTimestamp = resetCurrentTimestamp();
            }
        } else {
            sequence = 0;
        }

        //刷新id生成时间
        lastTimestamp = currentTimestamp;

        // currentTimestamp - TIME_ANCHOR 时间部分；workerNum 机器号部分；serverNum 服务器部分；sequence 序列号部分
        return ((currentTimestamp - TIME_ANCHOR) << 22) | (workerNumber << 17) | (serverNumber << 12) | sequence;
    }

    /**
     * 重置当前时间戳
     */
    private long resetCurrentTimestamp() {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    //---------------测试---------------
    public static void main(String[] args) {
        int num = 2;
        SnowFlakeGenerator worker = new SnowFlakeGenerator();
        for (int i = 0; i < num; i++) {
        System.out.println(worker.generatorId());
        }
    }

}
